/**
 * 客户端测试用户模型
 *
 * Created by snowingsea on 2020/01/22.
 */

const request = require('request-promise');
const querystring = require('querystring');
const randomstring = require('randomstring');
const User = require('./user');
const Project = require('./project');
const ProjectVersion = require('./project-version');

const testerURI = require('./../api/tester/uri');

const projects = require('./../../phoenix/models/projects');
const tasks = require('./../../phoenix/models/tasks');

const data = require('./../benchmark/ephemeral-data/data.json');

class Tester extends User {
  /**
   * 获取项目信息
   * @param {String} projectId 项目id
   * @return {Promise}
   */
  getProject(projectId) {
    return new Promise((resolve, reject) => {
      let project;
      this.login()
        .then(() => {
          const options = {
            url: `${testerURI}/projects/${projectId}.json`,
            headers: { Authorization: `Bearer ${this.token.accessToken}` },
            json: true,
          };
          return request.get(options);
        })
        .then((res) => {
          project = res;
          return this.logout();
        })
        .then(() => {
          resolve(project);
        })
        .catch(reject);
    });
  }

  /**
   * 获取编译配置选项
   * @return {Promise}
   */
  getCompilationOptions() {
    return new Promise((resolve, reject) => {
      let compilationOptions;
      this.login()
        .then(() => {
          const options = {
            url: `${testerURI}/machine/compilation-options.json`,
            headers: { Authorization: `Bearer ${this.token.accessToken}` },
            json: true,
          };
          return request.get(options);
        })
        .then((res) => {
          compilationOptions = res;
          return this.logout();
        })
        .then(() => {
          resolve(compilationOptions);
        })
        .catch(reject);
    });
  }

  /**
   * 获取当前用户所有项目列表
   * @return {Promise}
   */
  getAllProjects() {
    return new Promise((resolve, reject) => {
      this.login()
        .then(() => {
          let page = 1;
          const allProjects = [];
          const that = this;
          function step() {
            request.get({
              url: `${testerURI}/projects.json?page=${page}`,
              headers: { Authorization: `Bearer ${that.token.accessToken}` },
              json: true,
            })
              .then(({ projects }) => {
                if (projects.length === 0) {
                  that.logout().then(() => { resolve(allProjects); }).catch(reject);
                  return;
                }
                page += 1;
                projects.forEach(({ projectId, projectName, status }) => {
                  allProjects.push(new Project(that, projectId, projectName, status));
                });
                step();
              })
              .catch(reject);
          }
          step();
        })
        .catch(reject);
    });
  }

  /**
   * 获取当前用户项目总数
   * @param {Object} filter 筛选器
   * @return {Promise}
   */
  getProjectTotalNumber(filter = {}) {
    return new Promise((resolve, reject) => {
      let totalNumber = 0;
      this.login()
        .then(() => {
          const url = `${testerURI}/projects.json?${querystring.stringify(filter)}`;
          return request.get({
            url,
            headers: { Authorization: `Bearer ${this.token.accessToken}` },
            json: true,
          });
        })
        .then(({ pagination }) => {
          totalNumber = pagination.total;
          return this.logout();
        })
        .then(() => {
          resolve(totalNumber);
        })
        .catch(reject);
    });
  }

  /**
   * 登录状态下创建项目
   * @param {String} projectName 项目名称
   * @param {String} language 项目类型
   * @param {String} projectType 编译器
   * @param {String} initVersion 初始版本
   * @return {Promise}
   */
  createProject(
    {
      projectName,
      language,
      projectType,
      initVersion = undefined,
    },
  ) {
    return new Promise((resolve, reject) => {
      const query = { projectName, language, projectType };
      if (initVersion) {
        query.initVersion = initVersion;
      }
      const options = {
        url: `${testerURI}/projects.json?${querystring.stringify(query)}`,
        headers: { Authorization: `Bearer ${this.token.accessToken}` },
        json: true,
      };
      request.post(options)
        .then((res) => {
          const project = new Project(this, res.projectId, res.projectName, res.status);
          const { versionId, versionName, status } = res.lastVersion;
          project.lastVersion = new ProjectVersion(project, versionId, versionName, status);
          resolve(project);
        })
        .catch(reject);
    });
  }

  /**
   * 随机创建项目
   * @param {String} projectLanguage 语言
   * @return {Promise}
   */
  createRandomProject(projectLanguage = 'c') {
    return new Promise((resolve, reject) => {
      let project;
      this.login()
        .then(() => this.createProject(Project.getRandomProjectInfo(projectLanguage)))
        .then((res) => {
          project = res;
          return this.logout();
        })
        .then(() => {
          resolve(project);
        })
        .catch(reject);
    });
  }

  /**
   * 批量创建随机项目
   * @param {Number} count 创建个数
   * @return {Promise}
   */
  createRandomProjects(count = 10) {
    return new Promise((resolve, reject) => {
      const projects = [];
      this.login()
        .then(() => {
          const that = this;
          function step(index) {
            if (index >= count) {
              that.logout().then(() => { resolve(projects); }).catch(reject);
              return;
            }
            that.createProject(Project.getRandomProjectInfo())
              .then((res) => {
                projects.push(res);
                step(index + 1);
              })
              .catch(reject);
          }
          step(0);
        })
        .catch(reject);
    });
  }

  getRules({
    languages = [],
    levels = [],
    keywords = undefined,
  } = {}) {
    return this.login()
      .then(() => {
        const url = `${testerURI}/static-analyze/rules.json`;
        const qs = {};
        if (languages && languages.length) {
          qs.languages = languages;
        }
        if (levels && levels.length) {
          qs.levels = levels;
        }
        if (keywords) {
          qs.keywords = keywords;
        }
        return request.get({
          url,
          qs,
          headers: { Authorization: `Bearer ${this.token.accessToken}` },
          json: true,
        });
      });
  }

  /**
   * 创建检测模板
   * @return {Promise}
   */
  createTemplate(externalRuleCodes = []) {
    return new Promise((resolve, reject) => {
      let analyzeTemplate;
      this.login()
        .then(() => {
          const url = `${testerURI}/static-analyze/rules.json`;
          return request.get({
            url,
            headers: { Authorization: `Bearer ${this.token.accessToken}` },
            json: true,
          });
        })
        .then((rules) => {
          const ruleIds = [];
          rules.forEach((ruleSet) => {
            ruleSet.levels.forEach((level) => {
              level.rules.forEach((rule) => {
                if (externalRuleCodes.indexOf(rule.ruleCode) >= 0
                  || externalRuleCodes.length === 0
                ) {
                  ruleIds.push(rule.ruleId);
                }
              });
            });
          });
          const url = `${testerURI}/static-analyze/templates.json`;
          return request.post({
            url,
            headers: { Authorization: `Bearer ${this.token.accessToken}` },
            json: true,
            form: {
              name: randomstring.generate(16),
              languages: ['c', 'c++'],
              description: randomstring.generate(100),
              ruleIds,
            },
          });
        })
        .then((template) => {
          analyzeTemplate = template;
          return this.logout();
        })
        .then(() => {
          resolve(analyzeTemplate);
        })
        .catch(reject);
    });
  }

  /**
   * 删除项目
   * @param {String} projectId 项目id
   * @return {Promise}
   */
  removeProject(projectId) {
    return new Promise((resolve, reject) => {
      this.login()
        .then(() => {
          const url = `${testerURI}/projects/${projectId}.json`;
          return request.delete({
            url,
            headers: { Authorization: `Bearer ${this.token.accessToken}` },
          });
        })
        .then(() => this.logout())
        .then(() => {
          resolve();
        })
        .catch(reject);
    });
  }

  /**
   * 更新项目信息
   * @param {String} projectId 项目id
   * @param {Object} options 更新信息
   * @return {Promise}
   */
  updateProject(projectId, options) {
    return new Promise((resolve, reject) => {
      this.login()
        .then(() => {
          const url = `${testerURI}/projects/${projectId}.json?${querystring.stringify(options)}`;
          return request.put({
            url,
            headers: { Authorization: `Bearer ${this.token.accessToken}` },
          });
        })
        .then(() => this.logout())
        .then(() => {
          resolve();
        })
        .catch(reject);
    });
  }

  /**
   * 冻结项目
   * @param {String} projectId 项目id
   * @return {Promise}
   */
  freezeProject(projectId) {
    return this.updateProject(projectId, { status: projects.statusKind.frozen });
  }

  /**
   * 随机冻结项目
   * @param {Array} projectArray 项目列表
   * @return {Promise}
   */
  randomFreezeProjects(projectArray) {
    return new Promise((resolve, reject) => {
      this.login()
        .then(() => {
          const promises = projectArray.map((project) => {
            if (Math.random() < 0.5) {
              return Promise.resolve();
            }
            const status = projects.statusKind.frozen;
            const url = `${testerURI}/projects/${project.projectId}.json?${querystring.stringify({ status })}`;
            return request.put({
              url,
              headers: { Authorization: `Bearer ${this.token.accessToken}` },
            });
          });
          return Promise.all(promises);
        })
        .then(() => this.logout())
        .then(resolve)
        .catch(reject);
    });
  }

  /**
   * 等待项目版本中的任务结束
   * @param {String} projectVersionId 项目版本id
   * @return {Promise}
   */
  waiting4TasksOfVersionFinished(projectVersionId) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        let finished = false;
        this.login()
          .then(() => {
            const query = { status: [tasks.statusKind.running, tasks.statusKind.waiting] };
            const options = {
              url: `${testerURI}/project-versions/${projectVersionId}/tasks.json?${querystring.stringify(query)}`,
              headers: { Authorization: `Bearer ${this.token.accessToken}` },
              json: true,
            };
            return request.get(options);
          })
          .then((res) => {
            finished = res.pagination.total === 0;
            return this.logout();
          })
          .then(() => {
            if (finished) {
              resolve();
            } else {
              this.waiting4TasksOfVersionFinished(projectVersionId).then(resolve).catch(reject);
            }
          })
          .catch(reject);
      }, 3000);
    });
  }

  /**
   * 存储临时数据
   * @return {Promise}
   */
  storageEphemeralData() {
    return new Promise((resolve, reject) => {
      this.login()
        .then(() => {
          const form = { data };
          return request.post({
            url: `${testerURI}/ephemeral-data.json`,
            headers: { Authorization: `Bearer ${this.token.accessToken}` },
            json: true,
            form,
          });
        })
        .then((res) => {
          this.logout();
          resolve(res);
        })
        .catch(reject);
    });
  }
}

/**
 * Export model definition object.
 */
module.exports = Tester;
